﻿using Newtonsoft.Json;
using System.Xml;
using Up6.core.Up6Manager.Biz;
using up6.utils;
using up6.db.biz;
using up6.sql.model;
using OBS.Model;

namespace up6.db.model;

public class FileInf
{
    /// <summary>
    /// 6.3版本新增字段
    /// </summary>
    [DataBase("f_id")]
    public string id = string.Empty;
    /// <summary>
    /// 文件夹ID
    /// </summary>
    [DataBase("f_pid")]
    public string pid = string.Empty;
    /// <summary>
    /// 根级文件夹ID
    /// </summary>
    [DataBase("f_pidRoot")]
    public string pidRoot = string.Empty;
    /// <summary>
    /// 表示当前项是否是一个文件夹项。
    /// </summary>
    [DataBase("f_fdTask")]
    public bool fdTask = false;
    /// <summary>
    /// 与xdb_folders.fd_id对应
    /// </summary>
    /// <summary>
    /// 是否是文件夹中的子文件
    /// </summary>
    [DataBase("f_fdChild")]
    public bool fdChild = false;
    /// <summary>
    /// 用户ID。与第三方系统整合使用。
    /// </summary>
    [DataBase("f_uid")]
    public string uid = string.Empty;
    /// <summary>
    /// 文件在本地电脑中的名称。
    /// </summary>
    [DataBase("f_nameLoc")]
    public string nameLoc = string.Empty;
    /// <summary>
    /// 文件在服务器中的名称。
    /// </summary>
    [DataBase("f_nameSvr")]
    public string nameSvr = string.Empty;
    /// <summary>
    /// 文件在本地电脑中的完整路径。示例：D:\Soft\QQ2012.exe
    /// </summary>
    [DataBase("f_pathLoc")]
    public string pathLoc = string.Empty;
    /// <summary>
    /// 文件在服务器中的完整路径。示例：F:\ftp\uer\md5.exe
    /// </summary>
    [DataBase("f_pathSvr")]
    public string pathSvr = string.Empty;
    /// <summary>
    /// 文件在服务器中的相对路径。示例：/www/web/upload/md5.exe
    /// </summary>
    [DataBase("f_pathRel")]
    public string pathRel = string.Empty;
    /// <summary>
    /// 文件MD5
    /// </summary>
    [DataBase("f_md5")]
    public string md5 = string.Empty;
    /// <summary>
    /// 数字化的文件长度。以字节为单位，示例：120125
    /// 文件大小可能超过2G，所以使用long
    /// </summary>
    [DataBase("f_lenLoc")]
    public long lenLoc = 0;
    /// <summary>
    /// 本地文件加密后的大小
    /// </summary>
    [DataBase("f_lenLocSec")]
    public long lenLocSec = 0;
    /// <summary>
    /// 格式化的文件尺寸。示例：10.03MB
    /// </summary>
    [DataBase("f_sizeLoc")]
    public string sizeLoc = string.Empty;
    /// <summary>
    /// 文件续传位置。
    /// 文件大小可能超过2G，所以使用long
    /// </summary>
    [DataBase("f_offset")]
    public long offset = 0;
    /// <summary>
    /// 已上传大小。以字节为单位
    /// 文件大小可能超过2G，所以使用long
    /// </summary>
    [DataBase("f_lenSvr")]
    public long lenSvr = 0;
    /// <summary>
    /// 已上传百分比。示例：10%
    /// </summary>
    [DataBase("f_perSvr")]
    public string perSvr = "0%";
    [DataBase("f_complete")]
    public bool complete = false;
    [JsonIgnore]
    public string token = string.Empty;
    public string key = string.Empty;
    public int cover = 0;
    [DataBase("f_time")]
    public DateTime time = DateTime.Now;
    [DataBase("f_deleted")]
    public bool deleted = false;
    /// <summary>
    /// 是否已经扫描完毕，提供给大型文件夹使用
    /// 大型文件夹上传完毕后开始扫描
    /// </summary>
    [DataBase("f_scan")]
    public bool scaned = false;
    /// <summary>
    /// 块索引，基于1
    /// </summary>
    [DataBase("f_blockIndex")]
    public int blockIndex = 0;
    /// <summary>
    /// 块偏移，基于整个文件
    /// </summary>
    [DataBase("f_blockOffset")]
    public long blockOffset = 0;
    /// <summary>
    /// 块加密后的偏移
    /// 算法：加密块大小 * 块索引(基于0)
    /// </summary>
    public long blockOffsetCry = 0;
    /// <summary>
    /// 块总数
    /// </summary>
    [DataBase("f_blockCount")]
    public int blockCount = 0;
    /// <summary>
    /// 块大小
    /// </summary>
    [DataBase("f_blockSize")]
    public int blockSize = 0;
    /// <summary>
    /// 块加密大小
    /// </summary>
    [DataBase("f_blockSizeSec")]
    public int blockSizeSec = 0;

    /// <summary>
    /// 块压缩大小
    /// </summary>
    public int blockSizeCpr = 0;
    /// <summary>
    /// 压缩方式，gzip,zip
    /// </summary>
    public string blockCprType = string.Empty;
    public string blockMd5 = string.Empty;
    [JsonIgnore]
    public string callback = string.Empty;

    /// <summary>
    /// 用于第三方存储的对象ID，如FastDFS,Minio等
    /// </summary>
    public String object_id = "";

    /// <summary>
    /// 对象存储的key
    /// </summary>
    [DataBase("f_object_key")]
    public String object_key = "";
    //
    public String etag = "";
    /// <summary>
    /// 文件是否加密存储
    /// </summary>
    [DataBase("f_encrypt")]
    public bool encrypt = false;
    /// <summary>
    /// 加密算法,sm4,aes
    /// </summary>
    [DataBase("f_encryptAgo")]
    public string encryptAgo = "";

    public Dictionary<string, string> fields = new Dictionary<string, string>();

    public string schemaFile()
    {
        return Path.Combine(this.parentDir(), "schema.txt");
    }

    /**
     * 生成AWS S3文件key
     * D:\Soft\guid\QQ.exe => /guid/QQ.exe
     * @return
     */
    public String S3Key()
    {
        //格式 /guid/QQ.exe
        String key = PathTool.combin("", this.id);
        key = PathTool.combin(key, this.nameLoc);
        return key;
    }

    public string ossKey()
    {
        //格式：guid/QQ.exe
        return PathTool.combin(this.id, this.nameLoc);
    }

    public string getObjectKey()
    {
        if (ConfigReader.storage() == StorageType.Minio) return this.S3Key();
        else if (ConfigReader.storage() == StorageType.OSS ||
            ConfigReader.storage() == StorageType.OBS) return this.ossKey();
        //fastdfs
        return this.object_id;
    }

    /**
     * 返回AWS s3，ETag保存文件
     * 路径：dir/guid/etags.txt
     * @return
     */
    public String ETagsFile()
    {
        String f = PathTool.combin(this.parentDir(), this.id);
        f = PathTool.combin(f, "etags.txt");
        return f;
    }

    public string parentDir()
    {
        return PathTool.parentDir(this.pathSvr);
    }

    public void saveScheme()
    {
        //仅第一块保存
        if (1 != this.blockIndex) return;
        //仅保存子文件数据
        if (string.IsNullOrEmpty(this.pid)) return;

        FolderSchema fs = new FolderSchema();
        fs.addFile(this);
    }

    /**
     * 保存块ID信息
     * 路径：
     * D:/Soft/guid/etags.txt
     */
    public void saveEtags()
    {
        FileEtag fe = new FileEtag();
        fe.saveTags(this);
    }

    /**
     * <CompleteMultipartUpload xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
       <Part>
          <ChecksumCRC32>string</ChecksumCRC32>
          <ChecksumCRC32C>string</ChecksumCRC32C>
          <ChecksumSHA1>string</ChecksumSHA1>
          <ChecksumSHA256>string</ChecksumSHA256>
          <ETag>string</ETag>
          <PartNumber>integer</PartNumber>
       </Part>
       ...
    </CompleteMultipartUpload>

     * @return
     */

    public String etags()
    {
        List<FileInf> blocks = new List<FileInf>();
        //加载/guid/etags.txt
        StreamReader sr = new StreamReader(this.ETagsFile());
        string line = String.Empty;
        Dictionary<int, bool> dic = new Dictionary<int, bool>();
        while ((line = sr.ReadLine()) != null)
        {
            var b = JsonConvert.DeserializeObject<FileInf>(line);
            //防止重复添加
            if (dic.ContainsKey(b.blockIndex)) continue;
            blocks.Add(b);
            dic.Add(b.blockIndex, true);
        }

        XmlDocument doc = new XmlDocument();
        var root = doc.CreateElement("CompleteMultipartUpload");
        foreach (FileInf b in blocks)
        {
            var part = doc.CreateElement("Part");
            var etag = doc.CreateElement("ETag");
            var number = doc.CreateElement("PartNumber");

            number.InnerText = b.blockIndex.ToString();
            etag.InnerText = b.etag;
            part.AppendChild(etag);
            part.AppendChild(number);
            root.AppendChild(part);
        }
        doc.AppendChild(root);

        return UtilsTool.xmlToString(ref doc);
    }

    public List<OBS.Model.UploadPartResponse> obsEtags()
    {
        List<FileInf> blocks = new List<FileInf>();
        //加载/guid/etags.txt
        StreamReader sr = new StreamReader(this.ETagsFile());
        string line = String.Empty;
        Dictionary<int, bool> dic = new Dictionary<int, bool>();
        while ((line = sr.ReadLine()) != null)
        {
            Console.WriteLine(line);
            var b = JsonConvert.DeserializeObject<FileInf>(line);
            //防止重复添加
            if (dic.ContainsKey(b.blockIndex)) continue;
            blocks.Add(b);
            dic.Add(b.blockIndex, true);
        }

        List<UploadPartResponse> etags = new List<UploadPartResponse>();
        foreach (FileInf b in blocks)
        {
            etags.Add(JsonConvert.DeserializeObject<OBS.Model.UploadPartResponse>(b.etag));
        }
        return etags;
    }

    public List<Aliyun.OSS.PartETag> ossEtags()
    {
        List<FileInf> blocks = new List<FileInf>();
        //加载/guid/etags.txt
        StreamReader sr = new StreamReader(this.ETagsFile());
        string line = String.Empty;
        Dictionary<int, bool> dic = new Dictionary<int, bool>();
        while ((line = sr.ReadLine()) != null)
        {
            Console.WriteLine(line);
            var b = JsonConvert.DeserializeObject<FileInf>(line);
            //防止重复添加
            if (dic.ContainsKey(b.blockIndex)) continue;
            blocks.Add(b);
            dic.Add(b.blockIndex, true);
        }

        List<Aliyun.OSS.PartETag> etags = new List<Aliyun.OSS.PartETag>();
        foreach (FileInf b in blocks)
        {
            Aliyun.OSS.PartETag et = new Aliyun.OSS.PartETag(b.blockIndex, b.etag);
            etags.Add(et);
        }
        return etags;
    }

    /// <summary>
    /// 块路径。
    /// folder/file.id/blockindex.part
    /// </summary>
    /// <returns></returns>
    public string blockPath()
    {
        var ps = PathTool.parentDir(this.pathSvr);
        ps = string.Format("{0}/{1}/{2}.part",
            ps,
            this.id,
            this.blockIndex);
        return ps;
    }

    public string formatSize(long byteCount)
    {
        string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" };
        if (byteCount == 0)
            return "0" + suf[0];
        long bytes = Math.Abs(byteCount);
        int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
        double num = Math.Round(bytes / Math.Pow(1024, place), 1);
        return (Math.Sign(byteCount) * num).ToString() + suf[place];
    }
}
